Effective-Objective-C读书笔记(5)

内存管理

第29条:理解引用计数

通过引用计数来管理内存,当对象使用 newallocretain,创建的时候,其 引用计数+1。当使用release的时候,引用计数-1。当其引用计数为0的时候,对象会被释放,同时需要将指针置 nil,防止出现悬垂指针

自动释放池:当对象调用 release 会立即递减对象的引用计数,有时候可以不调用它。改为调用 autorelease,此方法会在稍后递减计数,通常会在下一次事件循环的时候递减。此方法可以保证对象在跨越方法调用边界后一定存活。使用 autorelease 可以延长对象生命周期。

1
2
3
4
5
6
7
- (NSString *)stringValue {
NSString *str = [[NSString alloc] initWithFormat:@"I am this: %@", self];
return [str autorelease];
}

// stringValue 返回调用者时,此对象必然存活。所以可以这样调用
NSString *str = [self stringValue];

由于返回的 str 对象稍后将自动释放,所以无需再执行内存管理操作。但是如果要持有该对象的话(比如要将其设置给实例变量),那就需要保留,并稍后释放:

1
_instanceVariable = [[self stringValue] retain];

第30条:用 ARC 简化引用计数

  • 在 ARC 环境中,引用计数实际上还是要执行的,只不过引用计数现在是由 ARC 自动添加。
  • 不能使用 retainreleaseautoreleasedealloc 方法。
  • 若方法名以 allocnewcopymutableCopy 开头,则返回的对象归调用者所有。
  • 不使用 ARC 情况下,在实现单例类的时候,因为单例不可以释放,所有我们经常个覆写 release 方法,将其替换为 空操作
  • ARC 下会借用 Objective-C++ 中的一项特性来清理对象,回收 Objective-C++ 对象时,待回收对象会调用 C++ 对象的析构函数。编译器如果发现对象里含有 C++ 对象,就会生成 .cxx_destruct 的方法。在此方法中生成清理内存的代码。
  • ARC 只负责管理 Objective-C 对象内存,CoreFoundation 对象不归 ARC 管理,开发者必须适时调用 CFRetain/CFRelease。

第31条:在 dealloc 方法中只释放引用并解除监听

  • 在 dealloc 方法中,ARC 会通过自动生成 .cxx_destruct 方法,并自动释放所有的 Objective-C 对象。
  • 在此方法中移除通知
  • 如果不使用 ARC ,那么最后还需要调用 [super dealloc]。ARC 会自动执行此操作。
  • 如果对象持有文件描述符等资源,应该撰文写一个方法来释放此种资源。这样的类要和使用者约定:用完资源后必须调用 close 方法。
  • 执行异步任务的方法不应该在 dealloc 里调用。只能在正常状态下执行的那些方法也不应该在 dealloc 里调用,因为此时对象已处在正在回收的状态了。

第32条:编写异常安全代码时要注意内存管理问题

  • 在非 ARC 下,使用@try...@catch来捕获异常,当异常发生时,程序直接执行@catch中的语句。忽略了@try中的内存管理语句。最好的做法是将@try中的内存管理语句放在 @finally 当中。
  • 默认情况下,ARC 不生成安全处理异常的所需要的清理代码。开启编译器-fobjc-arc-exceptions标志后,可以生成这种代码,不过会导致应用程序变大。降低运行效率。

第33条:用弱引用避免循环引用

  • 将某些引用设置为 weak,避免循环引用。
  • weak 引用可以自动清空,也可以不自动清空。自动清空是随着 ARC 而引入的新特性,由运行时系统来实习那。在自动清空功能的弱引用上,可以随意读取其数据,因为这种引用不会指向已经回收过的对象。

第34条:使用 autoreleasepool 来降低内存峰值

  • 当程序当中需要创建大量的临时对象(比如循环),会造成内存增长,可以考虑将临时代码放在autoreleasepool当中,那么在循环中自动释放的对象就会放在这个池,而不是线程的主池里面。
  • autoreleasepool存放在栈中,对象收到 autorelease 消息后,系统将其放入最顶端的池里。

第35条:用僵尸对象调试内存管理问题

  • 向已经回收的对象发送消息是不安全的,因为这有时可以,有时不行。完全取决于对象所占内存有没有为其他内容所覆写,而这块内存有没有移作他用,又无法确定。此时需要开启僵尸对象进行检查。
  • 系统在回收对象的时候,可以不将其真的回收,而是把它转化为僵尸对象。通过 NSZombieEnabled 可开启此功能。

第36条:不要使用 retainCount

  • 不要使用 retainCount 来查看对象是否还被持有,因为任何给定时间点上 绝对引用计数 都无法反应对象生命周期的全貌。引用计数可能处在自动释放池中,retainCount 可能不准确。
  • ARC 后,retainCount 就被废止了。